/* 

 * @author    ThemePunch <info@themepunch.com>

 * @link      http://www.themepunch.com/

 * @copyright 2017 ThemePunch

*/



;(function() {

	

	var $, 

		engine,

	    requestFrame = window.requestAnimationFrame;



		 

	window.RsAddonPanorama = function(_$, slider) {

		

		if(!engine) {

			

			engine = webGL();

			if(!engine) {

				

				slider.addClass('pano-no-webgl');

				return;

				

			}

			

		}

		

		if(!_$ || !slider || typeof THREE === 'undefined' || !requestFrame) return;

		if(!slider.find('li[data-panorama]').length) return;

		

		$ = _$;

		$.event.special.rsPanoramaDestroyed = {remove: function(evt) {evt.handler();}};

		

		return new Panorama(slider);

		

	};

	

	function Panorama(slider) {

		

		this.slider = slider;	

		this.slides = [];

		this.panos = slider.find('li[data-panorama]').each(function(i) {

			

			if(i === 0) this.setAttribute('data-fstransition', 'notransition');

			this.setAttribute('data-transition', 'fade');

			

		});

		

		slider.one('revolution.slide.onloaded', this.onLoaded.bind(this))

			  .one('rsPanoramaDestroyed', this.destroy.bind(this));

		

	}

	

	Panorama.prototype = {

		

		onLoaded: function() {

			

			this.slider.one('revolution.slide.onchange', this.onReady.bind(this));

			jQuery(window).on('blur.rsaddonpanorama', this.onBlur.bind(this));

			

		},

		

		onReady: function(e, data) {

			

			this.resize();

			var $this = this;

			this.panos.each(function(i) {

				

				$this.slides[i] = new PanoramaSlide($(this), $this.slider, $this.width, $this.height);

				

			});

			

			this.slider.on('revolution.slide.afterdraw', this.resize.bind(this))

			           .on('revolution.slide.onbeforeswap', this.beforeSwap.bind(this))

					   .on('revolution.slide.onafterswap', this.afterSwap.bind(this))

					   .on('layeraction', this.layerAction.bind(this));

					   

			var slide = $.data(data.currentslide[0], 'rsaddonpanorama');

			if(slide) slide.onReset();

			this.panos = null;

			

		},

		

		beforeSwap: function(e, data) {

			

			var slide = $.data(data.currentslide[0], 'rsaddonpanorama');

			if(slide && slide.controls) slide.removeEvents();

			

			slide = $.data(data.nextslide[0], 'rsaddonpanorama');

			if(slide) slide.onReset();

			

		},

		

		afterSwap: function(e, data) {

			

			this.currentSlide = $.data(data.currentslide[0], 'rsaddonpanorama');

			if(!data.prevslide.length) return;

			

			var slide = $.data(data.prevslide[0], 'rsaddonpanorama');

			if(slide) slide.paused = true;

			

		},

		

		resize: function() {

			

			this.width = this.slider.width();

			this.height = this.slider.height();

			

			var i = this.slides.length,

			    slide;

				

			while(i--) {

				

				slide = this.slides[i];

				slide.resize(this.width, this.height);

				

			}

			

		},

		

		onBlur: function() {

			

			var i = this.slides.length,

			    slide;

				

			while(i--) {

				

				slide = this.slides[i];

				slide.clear();

				slide.canvas.trigger('mouseup').trigger('mouseleave').trigger('touchend');

				

			}

			

		},

		

		layerAction: function(e, data) {

			

			data = data.event;

			var action = data.action;

			

			if(action && action.search('panorama') !== -1 && this.currentSlide) {

				

				var perc = data.hasOwnProperty('percentage') ? data.percentage * 0.01 : false;

				this.currentSlide.action(action.replace('panorama_', ''), perc);

				

			}

			

		},

		

		destroy: function() {

			

			if(this.slides) {

			

				while(this.slides.length) {

					

					this.slides[0].destroy();

					this.slides.shift();

					

				}

				

			}

			

			for(var prop in this) if(this.hasOwnProperty(prop)) delete this[prop];

			

		}

	

	};

	

	function PanoramaSlide(slide, slider, width, height) {

		

		var options = JSON.parse(slide.attr('data-panorama'));

		

		this.x = 0;

		this.y = 0;

		this.zoomVal = 1;

		this.width = width;

		this.height = height;

		this.slide = slide;

		this.slider = slider;

		this.distX = false;

		this.distY = false;

		

		this.image = options.image;

		this.controls = options.controls;

		this.drag = this.controls === 'drag';

		this.thrw = this.controls === 'throw';

		this.move = this.drag || this.thrw;

		this.mouse = this.controls === 'mouse';

		this.click = this.controls === 'click';

		this.autoplay = options.autoplay == 'true';

		this.zoom = options.mousewheelZoom == 'true';

		this.smoothZoom = options.smoothZoom == 'true';

		

		var direction = options.autoplayDirection === 'forward' ? 1 : -1;

		this.autoplaySpeed = parseInt(options.autoplaySpeed, 10) * 0.001;

		this.speed = this.autoplaySpeed * direction;

		

		this.throwSpeed = parseInt(options.throwSpeed, 10) * 0.001;

		this.zoomMax = 2 - (parseInt(options.zoomMax, 10) * 0.01);

		this.zoomMin = 2 - (parseInt(options.zoomMin, 10) * 0.01);

		this.fov = parseInt(options.cameraFov, 10);

		

		this.onStart = this.start.bind(this);

		this.onZoom = this.zooming.bind(this);

		this.inZoom = this.zoomIn.bind(this);

		this.outZoom = this.zoomOut.bind(this);

		this.onRender = this.render.bind(this);

		this.onMouse = this.mouseEvent.bind(this);

		this.onImgChange = this.imgChange.bind(this);

		this.fireAction = this.onAction.bind(this);

		

		this.renderer = new THREE[engine]();

		this.renderer.setSize(width, height);

		

		this.camera = new THREE.PerspectiveCamera(this.fov, width / height, 1, parseInt(options.cameraFar, 10));

		this.camera.target = new THREE.Vector3(0, 0, 0);

		

		var sphere = new THREE.SphereGeometry(parseInt(options.sphereRadius, 10), parseInt(options.sphereWsegments, 10), parseInt(options.sphereHsegments, 10));

		this.texture = new THREE.TextureLoader();

		this.texture.minFilter = THREE.LinearFilter;

			

		sphere.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));

		this.material = new THREE.MeshBasicMaterial();

		

		this.scene = new THREE.Scene();

		this.scene.add(new THREE.Mesh(sphere, this.material));

		

		$.data(slide[0], 'rsaddonpanorama', this);

		this.canvas = $(this.renderer.domElement).addClass('rsaddon-panorama').appendTo(slide.find('.slotholder'));

		

		slide.on('mouseleave', this.onLeave.bind(this)).find('.tp-withaction').each(setActions);

		

	}

	

	PanoramaSlide.prototype = {

		

		start: function() {

			

			if(!this.imgLoaded) this.imgLoaded = true;

			if(!this.currentSlide) return;

			

			if(this.newImage) {

			

				this.x = 0;

				this.y = 0;

				this.zoomVal = 1;

				this.camera.fov = this.fov;

				this.camera.updateProjectionMatrix();

				this.newImage = false;

				

			}

			

			this.controller = false;

			this.paused = false;

			

			// this.slider.removeClass('rsaddon-panorama-loading');

			requestFrame(this.onRender);

			

			this.addEvents();

			this.running = true;

			this.slider.addClass('rsaddon-pano');

			

		},

		

		render: function() {

			

			if(this.paused || !this.camera) return;

			this.y = Math.max(-85, Math.min(85, this.y));

			

			if(this.distX !== false || this.distY !== false) {

				

				if(this.distX !== false) {

				

					this.x += (this.distX - this.x) * 0.05;

					if(Math.abs(this.distX - this.x) < 1) {

						

						this.distX = false;

						clearInterval(this.actionTimer);

						

					}

					

				}

				else if(this.distY !== false) {

					

					this.y += (this.distY - this.y) * 0.05;

					if(Math.abs(this.distY - this.y) < 1) {

						

						this.distY = false;

						clearInterval(this.actionTimer);

						

					}

					

				}

				

			}

			else if(!this.controller) {



				if(this.autoplay) this.x += this.speed;

				

			}

			else if(!this.move) {



				var xx = (this.mouseX - this.left) / this.width,

				    yy = (this.mouseY - this.top) / this.height;

				

				xx = xx <= 0.5 ? (1 - (xx * 2)) * -180 : ((xx - 0.5) * 2) * 180;

				yy = yy <= 0.5 ? (1 - (yy * 2)) * 85 : ((yy - 0.5) * 2) * -85;

				yy = Math.max(-85, Math.min(85, yy));

				

				this.x += (xx - this.x) * 0.05;

				this.y += (yy - this.y) * 0.05;

				

				if((this.click || this.mouse) && Math.abs(xx - this.x) < 1 && Math.abs(yy - this.y) < 1) this.controller = false;

				

			}

			else if(this.thrw) {



				if(this.mousedown) {

					

					this.vx = this.x - this.oldX;

					this.vy = this.y - this.oldY;

					this.oldX = this.x;

					this.oldY = this.y;

					

				}

				else {

					

					var vx = Math.abs(this.vx),

					    vy = Math.abs(this.vy);

					

					if(vx > 0.01) this.x += this.vx;

					if(vy > 0.01) this.y += this.vy;

					

					this.vx *= this.throwSpeed;

					this.vy *= this.throwSpeed;

					

					if(vx <= 0.01 && vy <= 0.01) this.controller = false;

				

				}

					

			}

			

			var x = THREE.Math.degToRad(this.x),

			    y = THREE.Math.degToRad(90 - this.y);

			

			this.camera.target.x = Math.sin(y) * Math.cos(x);

			this.camera.target.z = Math.sin(y) * Math.sin(x);

			this.camera.target.y = Math.cos(y);

			this.camera.lookAt(this.camera.target);

			

			if(this.smoothZoom) { 

			

				this.camera.fov += ((this.fov * this.zoomVal) - this.camera.fov) * 0.05;

				this.camera.updateProjectionMatrix();

				

			}

			

			this.renderer.render(this.scene, this.camera);

			requestFrame(this.onRender);

			

		},

		

		onLeave: function() {

			

			clearInterval(this.actionTimer);

			clearInterval(this.zoomTimer);

			

		},

		

		clear: function() {

			

			clearInterval(this.actionTimer);

			clearInterval(this.zoomTimer);

			

			this.distX = false;

			this.distY = false;

			

		},

		

		onAction: function() {

			

			this.action(this.actionType, this.actionPerc);

			

		},

		

		action: function(tpe, perc) {

			

			var actionType,

				dist;

				

			switch(tpe) {

				

				case 'left':

					

					this.distX = this.x + (-180 * perc);

				

				break;

				

				case 'right':

					

					this.distX = this.x + (180 * perc);

				

				break;

				

				case 'up':

					

					dist = this.y + (85 * perc);

					this.distY = Math.max(-85, Math.min(85, dist));

				

				break;

				

				case 'down':

					

					dist = this.y + (-85 * perc);

					this.distY = Math.max(-85, Math.min(85, dist));

				

				break;

				

				case 'leftstart':

					

					actionType = 'left';

				

				break;

				

				case 'rightstart':

					

					actionType = 'right';

				

				break;

				

				case 'upstart':

					

					actionType = 'up';

				

				break;

				

				case 'downstart':

					

					actionType = 'down';

				

				break;

				

				case 'leftend':

				case 'rightend':

				case 'upend':

				case 'downend':

				

					clearInterval(this.actionTimer);

				

				break;

				

				case 'zoomin':

					

					clearInterval(this.zoomTimer);

					this.zooming('gestureend', 0.5);

				

				break;

				

				case 'zoomout':

					

					clearInterval(this.zoomTimer);

					this.zooming('gestureend', 1.5);

				

				break;

				

				case 'zoominstart':

					

					clearInterval(this.zoomTimer);

					this.inZoom();

					this.zoomTimer = setInterval(this.inZoom, 100);

				

				break;

				

				case 'zoomoutstart':

					

					clearInterval(this.zoomTimer);

					this.outZoom();

					this.zoomTimer = setInterval(this.outZoom, 100);

				

				break;

				

				case 'zoominend':

				case 'zoomoutend':

				

					clearInterval(this.zoomTimer);

				

				break;

				

			}

			

			if(actionType) {

				

				this.clear();

				this.actionPerc = perc;

				this.actionType = actionType;

				this.fireAction();

				this.actionTimer = setInterval(this.fireAction, 100);

				

			}

			

		},

		

		zooming: function(e, scale) {

			

			var tpe;

			this.prevZoom = this.zoomVal;

			

			if(e !== 'gestureend') {

				

				tpe = e.type;

				scale = e.scale;

				

			}

			else {	

			

				tpe = e;

				

			}

			

			if(tpe !== 'gestureend') {

			

				if(e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0) this.zoomVal -= 0.05;

				else this.zoomVal += 0.05;

				

			}

			else {

				

				

				if(scale < 1) this.zoomVal -= 0.05;

				else this.zoomVal += 0.05;

				

			}

			

			this.zoomVal = Math.max(this.zoomMax, Math.min(this.zoomMin, this.zoomVal));

			if(this.zoomVal === this.zoomMax || this.zoomVal === this.zoomMin) clearInterval(this.zoomTimer);

			

			if(!this.smoothZoom) {

				

				this.camera.fov = this.fov * this.zoomVal;

				this.camera.updateProjectionMatrix();

				

			}



			return false;

			

		},

		

		zoomIn: function() {

			

			this.zooming('gestureend', 0.5);

			

		},

		

		zoomOut: function() {

			

			this.zooming('gestureend', 1.5);

			

		},

		

		imgChange: function() {

			

			this.timer = setTimeout(this.onStart, 500);

			

		},

		

		update: function(prop, val, callback) {

			

			clearTimeout(this.timer);

			

			this.removeEvents();

			this.paused = true;

			

			switch(prop) {

				

				case 'image':

					

					// this.slider.addClass('rsaddon-panorama-loading');

					this.newImage = true;

					this.material.map = this.texture.load(val, this.onImgChange);

				

				break;

				

				case 'controls':

					

					this.controls = val;

					if(val !== 'none') {

					

						this.drag = val === 'drag';

						this.thrw = val === 'throw';

						this.mouse = val === 'mouse';

						this.click = val === 'click';

						this.move = this.drag || this.thrw;

						

					}

					else {

						

						this.x = 0;

						this.y = 0;

						

					}

				

				break;

				

				case 'autoplay':

				

					this.autoplay = val == 'true';

				

				break;

				

				case 'direction':

				

					var direction = val === 'forward' ? 1 : -1;

					this.speed = this.autoplaySpeed * direction;

				

				break;

				

				case 'zoom':

					

					if(val == 'false') {

						

						delete this.zoom;

						this.zoomVal = 1;

						this.camera.fov = this.fov;

						this.camera.updateProjectionMatrix();

						

					}

					else {

					

						this.zoom = true;

						

					}

				

				break;

				

				case 'smooth':

				

					this.smoothZoom = val == 'true';

				

				break;

			

			}

			

			if(prop !== 'image') this.timer = setTimeout(this.onStart, 500);

			

		},

		

		resize: function(width, height) {

			

			this.width = width;

			this.height = height;

			this.renderer.setSize(width, height);

			this.camera.aspect = width / height;

			this.camera.updateProjectionMatrix();

			

		},

		

		onReset: function() {

			

			this.paused = true;

			this.currentSlide = true;

			

			this.x = 0;

			this.y = 0;

			this.distX = false;

			this.distY = false;

				

			this.zoomVal = 1;

			this.camera.fov = this.fov;

			this.camera.updateProjectionMatrix();

			

			if(!this.imgLoaded) this.material.map = this.texture.load(this.image, this.onStart);

			else this.start();

			

		},

		

		enterMouse: function(e, triggered) {

			

			if(this.move) this.canvas.removeClass('rsaddon-panorama-drag').addClass('rsaddon-panorama-dragging');

			

			this.clear();

			this.controller = true;

			this.mousedown = true;

			

			var offset = this.slider.offset();

			this.left = offset.left;

			this.top = offset.top;

			

			if(!triggered) {

				

				var touch = e.originalEvent.touches;

				if(touch) e = touch[0];

				

				this.startX = e.pageX - this.left;

				this.startY = e.pageY - this.top;

			

				this.prevX = this.x;

				this.prevY = this.y;

				

				this.oldX = this.x;

				this.oldY = this.y;

				

				this.canvas.on('mousemove.rsaddonpanorama touchmove.rsaddonpanorama', this.onMouse); 

				

			}

			

		},

		

		mouseEvent: function(e) {

			

			this.onLeave();

			if(e.type.search(/move|click/) !== -1) {

				 

				if(this.move) {	

					if(!this.controller || !this.mousedown) return;

				}

				else if(!this.controller) {

					this.enterMouse(e, true);

				}

				

				var touch = e.originalEvent.touches;

				if(touch) e = touch[0];

				

				if(!this.move) {

					

					this.mouseX = e.pageX;

					this.mouseY = e.pageY;

					

				}

				else {

				

					this.x = this.prevX + ((this.startX - (e.pageX - this.left)) * 0.1);

					this.y = this.prevY + (((e.pageY - this.top) - this.startY) * 0.1);

					

				}

				

			}

			else if(e.type.search(/down|enter|start/) !== -1) {

				

				this.enterMouse(e);

				

			}

			else {



				this.canvas.off('mousemove.rsaddonpanorama touchmove.rsaddonpanorama'); 

				if(this.drag) this.controller = false;

				

				this.mousedown = false;

				if(this.move) this.canvas.removeClass('rsaddon-panorama-dragging').addClass('rsaddon-panorama-drag');

					

			}

			

		},

		

		addEvents: function() {

			

			switch(this.controls) {

				

				case 'drag':

				case 'throw':

					

					this.canvas.on(

					

						'mousedown.rsaddonpanorama ' + 

						'mouseup.rsaddonpanorama ' + 

						'mouseleave.rsaddonpanorama ' + 

						'touchstart.rsaddonpanorama ' + 

						'touchend.rsaddonpanorama ' + 

						'touchcancel.rsaddonpanorama', 

						

					this.onMouse);

				

				break;

				

				case 'click':

					

					this.slide.on('click.rsaddonpanorama', this.onMouse);

				

				break;

				

				case 'mouse':



					this.slide.on('mousemove.rsaddonpanorama touchmove.rsaddonpanorama', this.onMouse);

				

				break;

				

			}

			

			if(this.zoom) this.slider.on('mousewheel.rsaddonpanorama DOMMouseScroll.rsaddonpanorama gestureend.rsaddonpanorama', this.onZoom);

			if(this.controls !== 'none') {

				

				if(this.move) this.canvas.addClass('rsaddon-panorama-drag');

				else if(this.click) this.slide.addClass('rsaddon-panorama-click');

				

			}

			

		},

		

		removeEvents: function() {

			

			this.clear();

			this.mousedown = false;

			this.controller = false;

			this.currentSlide = false;

			

			switch(this.controls) {

				

				case 'drag':

				case 'throw':

				

					this.canvas.off('.rsaddonpanorama');

				

				break;

				

				case 'mouse':

				case 'click':

				

					this.slide.off('.rsaddonpanorama');

				

				break;

				

			}

				

			this.slider.off('.rsaddonpanorama');

			this.slide.removeClass('rsaddon-panorama-click');

			this.canvas.removeClass('rsaddon-panorama-drag rsaddon-panorama-dragging');

			

		},

		

		destroy: function() {

			

			for(var prop in this) if(this.hasOwnProperty(prop)) delete this[prop];

			

		}

		

	};

	

	function stopBubble(e) {

		

		e.stopPropagation();

		

	}

	

	function setActions() {

		

		var $this = jQuery(this),

			actions = $this.data().actions;

			

		if($this.hasClass('rspanoaction') || !Array.isArray(actions)) return;

		

		var i = actions.length,

			action;

			

		while(i--) {

			

			action = actions[i];

			if(action.action.search('panorama') !== -1) $this.addClass('rspanoaction').on('mousedown click mousemove touchmove', stopBubble);

			

		}

		

	}

	

	function webGL() {

		

		var canvas;

		try {



			canvas = document.createElement('canvas'); 

			canvas = !!(window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));

			if(canvas) canvas = 'WebGLRenderer';



		} 

		catch(e) {}

		

		if(!canvas) {

			

			canvas = window.CanvasRenderingContext2D;

			if(canvas) canvas = 'CanvasRenderer';

			

		}

		

		return canvas;

		

	}

	

})();























